package com.framework.utils.property;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.framework.utils.constant.Constants;
import com.framework.utils.util.StringUtils;

/**
 * 
 * <p>
 * Description: 配置文件注册管理
 * </p>
 * 
 * @since 2016年11月1日
 * @author lenovo
 *         <p>
 *         Copyright:Copyright(c)2016
 *         </p>
 */
public class ConfigFileManager {

	private static final Logger LOGGER = LoggerFactory.getLogger(ConfigFileManager.class);

	/**
	 * 配置文件的路径为:config/
	 */
	public static final String CONFIG_PATH = System.getProperty("user.dir") + File.separator + "config"
			+ File.separator;

	/**
	 * 暂存所有配置文件的配置项数据
	 */
	private final static Map<String, Properties> configFileMap = new HashMap<String, Properties>();

	/**
	 * 监听的线程
	 */
	private static PropertyWatchDog propertyWatchDog;

	/**
	 * 注册自定义配置文件时使用的回调监听对象
	 */
	private static CustomConfigFileAware customConfigFileAware;

	static {
		// 监听线程
		propertyWatchDog = new PropertyWatchDog();
		propertyWatchDog.start();
		// 注册自定义配置文件时使用的回调监听对象
		customConfigFileAware = new CustomConfigFileAware();

		// 注册配置文件，配置文件需要放在config目录下
		registerConfigFiles(Constants.MESSAGE_PROPERTY, "system/log_messages.xml");
		registerConfigFiles(Constants.LOG4J_PROPERTY, "system/log4j.properties");
		registerConfigFiles(Constants.SYSCONFIG_PROPERTY, "system/sysconfig.properties");

		// 注册自定义配置文件
		registerConfigFileWithCallBack(Constants.CUSTOM_REGISTER_CONFIG, "system/config_file_register.xml",
				customConfigFileAware);
	}

	/**
	 * 
	 * 方法用途: 注册需要监听的文件，可以添加多个文件<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param fileName
	 */
	public static void registerConfigFiles(String configName, String... fileNames) {
		if (fileNames == null || fileNames.length == 0) {
			LOGGER.info("配置文件不能为空");
			return;
		}

		propertyWatchDog.addConfigFiles(configName, fileNames);
	}

	/**
	 * 
	 * 方法用途: 注册具有回调事件的配置项<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param fileName
	 * @param reloadObj
	 */
	public static void registerConfigFileWithCallBack(String configName, String fileName, ReloadAware reloadObj) {
		if (fileName == null || reloadObj == null) {
			LOGGER.info("配置文件或回调对象不能为空");
			return;
		}

		propertyWatchDog.addConfigFileWithCallBack(configName, fileName, reloadObj);
	}

	/**
	 * 
	 * 方法用途: 注册具有回调事件的配置项<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param fileName
	 * @param reloadObj
	 */
	public static void registerConfigFileWithCallBack(String configName, String[] fileNames, ReloadAware reloadObj) {
		if (fileNames == null || fileNames.length == 0 || reloadObj == null) {
			LOGGER.info("配置文件或回调对象不能为空");
			return;
		}

		propertyWatchDog.addConfigFileWithCallBack(configName, fileNames, reloadObj);
	}

	/**
	 * 
	 * 方法用途: 重新加载属性文件，注意:fileName是文件的全路径<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param fileName
	 */
	public static void reload(String configName, String fileName) {
		// 从缓存中取出存在的配置项
		Properties propertyMap = configFileMap.get(configName);
		if (propertyMap == null) {
			propertyMap = new Properties();
			configFileMap.put(configName, propertyMap);
		}

		InputStream is = null;
		try {
			Properties newProperties = new Properties();
			is = new FileInputStream(fileName);
			if (fileName.endsWith(".xml")) {
				newProperties.loadFromXML(is);
			} else if (fileName.endsWith(".properties")) {
				newProperties.load(is);
			} else {
				LOGGER.info("配置文件格式不正确：" + fileName);
			}

			propertyMap.putAll(newProperties);
		} catch (Exception e) {
			LOGGER.info("加载日志配置文件：" + fileName + "错误，" + e.getMessage());
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 
	 * 方法用途: 获取某个配置文件内的所有属性的值<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 *            配置名称
	 * @return
	 */
	public static Properties getConfigFileProperties(String configName) {
		Properties properties = configFileMap.get(configName);
		if (properties == null) {
			properties = new Properties();
			configFileMap.put(configName, properties);
		}
		return properties;
	}

	/**
	 * 
	 * 方法用途: 获取某个配置的属性值<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param propertyName
	 * @return
	 */
	public static String getProperty(String configName, String propertyName) {
		Properties propertyMap = configFileMap.get(configName);
		if (propertyMap == null || propertyMap.isEmpty()) {
			return null;
		}

		return (String) propertyMap.get(propertyName);
	}

	/**
	 * 方法用途: 获取某个配置的属性值<br>
	 * 实现步骤: <br>
	 * 
	 * @param configName
	 * @param propertyName
	 * @param formatArgs
	 *            格式化属性参数
	 * @return
	 */
	public static String getProperty(String configName, String propertyName, Object... formatArgs) {
		Properties propertyMap = configFileMap.get(configName);
		if (propertyMap == null || propertyMap.isEmpty()) {
			return null;
		}
		String msg = (String) propertyMap.get(propertyName);
		if (null != formatArgs)
			msg = MessageFormat.format(msg.toString(), formatArgs);
		return msg;
	}

	/**
	 * 
	 * 方法用途: 设置监听事件检测文件的间隔时间，单位毫秒<br>
	 * 实现步骤: <br>
	 * 
	 * @param delay
	 */
	public static void setWatchInterval(long delay) {
		propertyWatchDog.setDelay(delay);
	}

	/**
	 * 
	 * 方法用途: 根据目录获取所有的配置项<br>
	 * 实现步骤: <br>
	 * 
	 * @param dir
	 * @return
	 */
	public static Map<String, Properties> getPropertyByDirectory(String dir) {
		if (dir == null || "".equals(dir)) {
			return null;
		}

		File dirFile = new File(CONFIG_PATH + dir);
		// 文件不存在或者不是文件夹
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return null;
		}

		Map<String, Properties> configPropertyMap = new HashMap<String, Properties>();
		// 文件绝对路径列表
		List<String> fileNamesList = new ArrayList<String>();
		getAllFiles(dirFile, fileNamesList);

		Set<String> configNameSet = new HashSet<String>();
		// 监听器中缓存的文件路径
		Map<String, List<String>> configFilePathMap = propertyWatchDog.getConfigFilePathMap();
		for (String fileName : fileNamesList) {
			// 缓存configName:监听的文件路径列表
			for (Map.Entry<String, List<String>> cacheConfigFileEntry : configFilePathMap.entrySet()) {
				if (cacheConfigFileEntry.getValue().contains(fileName)) {
					configNameSet.add(cacheConfigFileEntry.getKey());
					break;
				}
			}
		}
		
		// 自定义注册文件，需要检索配置文件路径对应的内容
		if (configNameSet.contains(Constants.CUSTOM_REGISTER_CONFIG)) {
			Properties prop = getConfigFileProperties(Constants.CUSTOM_REGISTER_CONFIG);
			for (Map.Entry<Object, Object> entry : prop.entrySet()) {
				configNameSet.add(entry.getKey().toString());
			}
		}

		for (String configName : configNameSet) {
			configPropertyMap.put(configName, ConfigFileManager.getConfigFileProperties(configName));
		}
		return configPropertyMap;
	}

	/**
	 * 
	 * 方法用途: 获取文件夹中，所有文件的文件名(包含子文件夹中的文件)<br>
	 * 实现步骤: <br>
	 * 
	 * @param fileDir
	 * @param fileNamesList
	 */
	private static void getAllFiles(File fileDir, List<String> fileNamesList) {
		if (fileDir.isDirectory()) {
			File[] files = fileDir.listFiles();
			for (File file : files) {
				if (file.isDirectory()) {
					getAllFiles(file, fileNamesList);
				} else {
					fileNamesList.add(StringUtils.replace(file.getAbsolutePath(), File.separator, "/"));
				}
			}
		} else {
			fileNamesList.add(StringUtils.replace(fileDir.getAbsolutePath(), File.separator, "/"));
		}
	}

	/**
	 * 
	 * <p>
	 * Description: 注册自定义配置文件后的回调类
	 * </p>
	 * 
	 * @since 2016年11月2日
	 * @author 张飞
	 *         <p>
	 *         Copyright:Copyright(c)2016
	 *         </p>
	 */
	private static class CustomConfigFileAware implements ReloadAware {

		@Override
		public void reload() {
			Properties customConfigProperty = ConfigFileManager
					.getConfigFileProperties(Constants.CUSTOM_REGISTER_CONFIG);
			for (Map.Entry<Object, Object> configEntry : customConfigProperty.entrySet()) {
				String configName = (String) configEntry.getKey();
				String fileNameStr = (String) configEntry.getValue();
				if (configName == null || "".equals(configName) || fileNameStr == null || "".equals(fileNameStr)) {
					LOGGER.info("配置项错误：" + configName + "  文件路径：" + fileNameStr);
					continue;
				}

				// 可以配置多个文件，使用逗号分隔
				String[] fileNameArr = fileNameStr.split(",");

				// 注册配置项
				registerConfigFiles(configName, fileNameArr);
			}
		}

	}

}
